package vn.appsmobi.ulti;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import vn.appsmobi.model.AppInfo;
import vn.appsmobi.model.LocalAppInfo;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.os.RemoteException;
import android.text.TextUtils;


public class LocalAppManager {
	 private static LocalAppManager sLocalAppManager;
	 private Context mContext;
	 private ConcurrentHashMap<String, LocalAppInfo> mInstalledApps;
	 private ConcurrentHashMap<String, LocalAppInfo> mInstalledNonSysApps;
	 private CopyOnWriteArraySet<LocalAppInfoUpdateListener> mListeners;
	 private ConcurrentHashMap<String, CopyOnWriteArraySet<LocalAppInstallRemoveListener>> mLocalAppListeners;
	 private ArrayList<LocalAppInfo> mInstalledSortList;
	/**Phong
	 * @param mContext
	 */
	public LocalAppManager(Context mContext) {
		this.mContext = mContext;
		this.mInstalledApps = new ConcurrentHashMap<String, LocalAppInfo>();
		this.mInstalledNonSysApps = new ConcurrentHashMap<String, LocalAppInfo>();
		this.mInstalledSortList = new ArrayList<LocalAppInfo>();
		this.mLocalAppListeners = new ConcurrentHashMap<String, CopyOnWriteArraySet<LocalAppInstallRemoveListener>>();
		loadInstalledApps(false);
	}
	
	
	public static void init(Context context)
	{
		if(sLocalAppManager == null)
			sLocalAppManager = new LocalAppManager(context);
	}
	 
	private void notifyContentChanged(AppInfo appinfo)
	  {
	    if (this.mListeners != null)
	    {	     
	      Iterator<LocalAppInfoUpdateListener> localIterator = this.mListeners.iterator();
	      while (localIterator.hasNext())
	        ((LocalAppInfoUpdateListener)localIterator.next()).onContentChanged(appinfo);
	    }
	  }
	
	public static LocalAppManager getManager()
	{
		return sLocalAppManager;
	}
	
	
	public boolean isInstalled(String packageName)
	 {
		Log.v("isInstalledpackageName:" + packageName);
	    if (TextUtils.isEmpty(packageName))
	    	return false;
	    else if(packageName.equals(GlobalApplication._().getPackageName()) || this.mInstalledApps.containsKey(packageName))
	    {
	    	Log.v("packageName:" + packageName);	    	
	    	return true;
	    }	      
	    else 
	    	return false;
	 }
	
	public AppInfo getLocalAppInfo(String packageName)
	  {
		AppInfo appInfo = null;
	    if (TextUtils.isEmpty(packageName))
	    	return null;
	    else
	    	appInfo = (AppInfo)this.mInstalledApps.get(packageName);
	    return appInfo;
	    
	  }
	
	public int getCount()
	{
		return this.mInstalledApps.size();
	}
	
	
	public ArrayList<LocalAppInfo> getInstalledApp()
	{
		return new ArrayList<LocalAppInfo>(mInstalledApps.values());
	}
	
	private void sortLocalApps()
	{
		
		//if(this.mInstalledApps.values().size() > 0)
		 //this.mInstalledSortList = new ArrayList<LocalAppInfo>(this.mInstalledApps.values());
	}
	public void loadInstalledApps(boolean includeSysApps) {
		includeSysApps = true;
		  mInstalledApps.clear();
		  Method getPackageSizeInfo = null;
	      // the package manager contains the information about all installed apps
	      PackageManager packageManager = this.mContext.getPackageManager();
	      try {
	          getPackageSizeInfo = packageManager.getClass().getMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class);
	      } catch (SecurityException e1) {

	          e1.printStackTrace();
	      } catch (NoSuchMethodException e1) {

	          e1.printStackTrace();
	      } 
	      List<PackageInfo> packs = packageManager.getInstalledPackages(0); //PackageManager.GET_META_DATA 
	      
	      for(int i=0; i < packs.size(); i++) {
	         PackageInfo p = packs.get(i);
	         ApplicationInfo a = p.applicationInfo;
	         // skip system apps if they shall not be included
	         if ((!includeSysApps) && ((a.flags & ApplicationInfo.FLAG_SYSTEM) == 1)) {
	            continue;
	         }
	         final LocalAppInfo app = new LocalAppInfo();
	         app.setName(p.applicationInfo.loadLabel(packageManager).toString());
	         //Log.v(p.applicationInfo.loadLabel(packageManager).toString() + "::" + p.packageName);
	         app.setPackageName(p.packageName);
	         app.setVersion(p.versionName);
	         app.setVersionCode(p.versionCode);	          
	         app.setSource(a.sourceDir);
	         //app.setHash();
	         CharSequence description = p.applicationInfo.loadDescription(packageManager);
	         app.setDescription(description != null ? description.toString() : "");
	         
	         try {
               getPackageSizeInfo.invoke(packageManager,  p.packageName, new IPackageStatsObserver.Stub()  {

                 public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {
              	   	 app.setSize((float) (pStats.codeSize/1024.0/1024.0));
                 }           
               });
             }catch(IllegalAccessException e) {}
             catch(IllegalArgumentException r) {}
             catch(InvocationTargetException w){}	
	        
	         mInstalledApps.put(app.getPackageName(), app);
	         sortLocalApps();
	      }	      
	   }

	 public void onPackageAdded(String packagename, int paramInt)
	  {
		 loadInstalledApps(false);
		 Log.v("on Package Added:" + packagename);
		 if (this.mLocalAppListeners.get(packagename) != null)
		    {
		      Iterator<LocalAppInstallRemoveListener> iterator = ((CopyOnWriteArraySet<LocalAppInstallRemoveListener>)this.mLocalAppListeners.get(packagename)).iterator();
		      while (iterator.hasNext())
		        ((LocalAppInstallRemoveListener)iterator.next()).onAppInstalled(packagename, paramInt);
		    }
		 
		 try{
			 AppInfo appinfo = AppInfo.getAppByPackage(packagename);
			 if(appinfo != null)
			 {
				 Log.v("onPackageAdded appinfo:" + appinfo.getName());				 
				 LocalAppManager.this.notifyContentChanged(appinfo);
			 }
			 
		 }catch(Exception e){
			 Log.v("onPackageAdded Exception" +  e.getMessage());
		 }
		 
	  }

	  public void onPackageModified(String packagename)
	  {
		  try{
			  Log.v("on Package Added:" + packagename);		
			 AppInfo appinfo = AppInfo.getAppByPackage(packagename);
			 if(appinfo != null)
				 LocalAppManager.this.notifyContentChanged(appinfo);
		  }catch(Exception e){ Log.v("onPackageModified Exception:"+ e.getMessage());}
		 
	  }

	  public void onPackageRemoved(String packagename, int uid)
	  {
		  loadInstalledApps(false);
		  Log.v("onPackageRemoved");
		   if (this.mLocalAppListeners.get(packagename) != null)
		    {
		      Iterator<LocalAppInstallRemoveListener> iterator = ((CopyOnWriteArraySet<LocalAppInstallRemoveListener>)this.mLocalAppListeners.get(packagename)).iterator();
		      while (iterator.hasNext())
		        ((LocalAppInstallRemoveListener)iterator.next()).onAppRemoved(packagename, uid);
		    }
		   try{
			   	AppInfo appinfo = AppInfo.getAppByPackage(packagename);
			   	if(appinfo != null)
			   		LocalAppManager.this.notifyContentChanged(appinfo);
		   }catch(Exception e){Log.v("onPackageRemoved Exception:"+ e.getMessage());}
			 
	  }
	  
	  
	 public void addLocalAppListener(String pakagename, LocalAppInstallRemoveListener listener)
	  {
	    if (listener == null)
	    {
	    	return;
	    }	      
	    else
	    {	    	
	      try
	      {
	        if (this.mLocalAppListeners.get(pakagename) == null)
	          this.mLocalAppListeners.put(pakagename, new CopyOnWriteArraySet<LocalAppInstallRemoveListener>());
	        ((CopyOnWriteArraySet<LocalAppInstallRemoveListener>)this.mLocalAppListeners.get(pakagename)).add(listener);
	      }
	      finally
	      {
	      }
	    }
	  }
	
	
	public void removeLocalAppListener(LocalAppInstallRemoveListener listener)
	  {
	    if (listener == null)
	    	return;
	    else	    			
	    {	      
	      try
	      {
	        Iterator<CopyOnWriteArraySet<LocalAppInstallRemoveListener>> iterator = this.mLocalAppListeners.values().iterator();
	        CopyOnWriteArraySet<LocalAppInstallRemoveListener> arrset = null;	        
	        do
	        {
		          if (!iterator.hasNext())
		            break;
		          arrset = (CopyOnWriteArraySet<LocalAppInstallRemoveListener>)iterator.next();
	        }while (!arrset.contains(listener));
	        
	        arrset.remove(listener);
	      }
	      finally
	      {
	      }
	    }
	  }
	  
	public static abstract interface LocalAppInfoUpdateListener
	  {
	    public abstract void onContentChanged();

	    public abstract void onContentChanged(AppInfo appInfo);

	    public abstract void onListChanged();

	    public abstract void onListChanged(AppInfo appInfo);

	    public abstract void onLocalInstalledLoaded();
	  }
	
	public static abstract interface LocalAppInstallRemoveListener
	  {
	    public abstract void onAppInstalled(String appid, int uid);

	    public abstract void onAppRemoved(String appid, int uid);
	  }
	
	public void addUpdateListener(LocalAppInfoUpdateListener mUpdateListener) {
		if (mUpdateListener != null);	    
	      {
	        if (this.mListeners == null)
	          this.mListeners = new CopyOnWriteArraySet<LocalAppInfoUpdateListener>();
	        this.mListeners.add(mUpdateListener);
	         
	        mUpdateListener.onListChanged();
	         
	        mUpdateListener.onContentChanged();
	      }
	      
	    }
		
	
}
